@tycoworks/tycoslide 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +10 -6
  2. package/dist/agents/catalog.d.ts +32 -0
  3. package/dist/agents/catalog.js +37 -0
  4. package/dist/agents/commands.d.ts +9 -0
  5. package/dist/agents/commands.js +57 -0
  6. package/dist/agents/extract.d.ts +22 -0
  7. package/dist/agents/extract.js +54 -0
  8. package/dist/{files.d.ts → agents/files.d.ts} +13 -3
  9. package/dist/{files.js → agents/files.js} +16 -3
  10. package/dist/agents/inventory.d.ts +83 -0
  11. package/dist/agents/inventory.js +568 -0
  12. package/dist/agents/manifest.d.ts +3 -0
  13. package/dist/{manifest.js → agents/manifest.js} +3 -14
  14. package/dist/agents/media.d.ts +38 -0
  15. package/dist/agents/media.js +83 -0
  16. package/dist/agents/pptx.d.ts +48 -0
  17. package/dist/agents/pptx.js +123 -0
  18. package/dist/agents/skill.d.ts +43 -0
  19. package/dist/agents/skill.js +134 -0
  20. package/dist/cli.js +8 -44
  21. package/dist/engine/dom.d.ts +3 -0
  22. package/dist/engine/dom.js +4 -0
  23. package/dist/engine/fillers/image.d.ts +6 -5
  24. package/dist/engine/fillers/image.js +24 -7
  25. package/dist/engine/generate.d.ts +2 -2
  26. package/dist/engine/generate.js +2 -2
  27. package/dist/engine/index.d.ts +3 -0
  28. package/dist/engine/index.js +2 -0
  29. package/dist/engine/notes.js +20 -62
  30. package/dist/engine/ooxml.d.ts +36 -0
  31. package/dist/engine/ooxml.js +56 -0
  32. package/dist/engine/types.d.ts +4 -0
  33. package/dist/index.d.ts +4 -11
  34. package/dist/index.js +2 -15
  35. package/dist/markdown/blocks/code.js +1 -2
  36. package/dist/markdown/blocks/image.d.ts +23 -0
  37. package/dist/markdown/blocks/image.js +65 -3
  38. package/dist/markdown/blocks/mermaid.d.ts +1 -1
  39. package/dist/markdown/blocks/mermaid.js +6 -4
  40. package/dist/markdown/blocks/table.js +7 -6
  41. package/dist/markdown/blocks/text.js +9 -8
  42. package/dist/markdown/deckCompiler.d.ts +2 -13
  43. package/dist/markdown/deckCompiler.js +7 -78
  44. package/dist/markdown/index.d.ts +4 -2
  45. package/dist/markdown/index.js +3 -1
  46. package/dist/markdown/inline.d.ts +2 -0
  47. package/dist/markdown/inline.js +4 -3
  48. package/dist/markdown/schema/themeConfigSchema.d.ts +2 -11
  49. package/dist/markdown/schema/themeConfigSchema.js +6 -16
  50. package/dist/markdown/slideParser.d.ts +2 -0
  51. package/dist/markdown/slideParser.js +6 -3
  52. package/dist/markdown/types.d.ts +4 -29
  53. package/dist/markdown/types.js +0 -11
  54. package/{theme-package/syntax.md → docs/markdown.md} +60 -28
  55. package/docs/theme.md +144 -0
  56. package/package.json +3 -2
  57. package/theme-package/SKILL.md +24 -90
  58. package/dist/engine/rels.d.ts +0 -18
  59. package/dist/engine/rels.js +0 -55
  60. package/dist/manifest.d.ts +0 -5
  61. package/dist/skillZip.d.ts +0 -49
  62. package/dist/skillZip.js +0 -177
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # tycoslide
2
2
 
3
- Create editable, on-brand PowerPoint slides from markdown, using your existing .pptx templates.
3
+ Create editable PowerPoint slides from markdown, using your existing .pptx templates.
4
4
 
5
5
  ## Getting started
6
6
 
7
- 1. **Create a tycoslide theme.** Install the [create-theme](skills/create-theme) skill (`npx skills add tycoworks/tycoslide`) and give an agent such as Claude Code or Codex your `.pptx`. You'll get back an npm package of mapped layouts, colors, and visual assets, like the [tycoworks-theme](https://github.com/tycoworks/tycoworks-theme).
7
+ 1. **Create a tycoslide theme.** Install the [create-theme](skills/create-theme) skill (`npx skills add tycoworks/tycoslide --skill create-theme`) and give an agent such as Claude Code or Codex your `.pptx`. You'll get back an npm package of mapped layouts, colors, and visual assets, like the [tycoworks-theme](https://github.com/tycoworks/tycoworks-theme).
8
8
  2. **Write slides in markdown.** GitHub-flavored markdown is supported; see [Markdown support](#markdown-support) below. tycoslide themes also include an agent skill, so an agent can write the slides for you.
9
9
  3. **Build.** `npx tycoslide build deck.md` compiles the markdown into an editable PowerPoint file.
10
10
 
@@ -18,17 +18,21 @@ npm install
18
18
  npx tycoslide build how-it-works.md
19
19
  ```
20
20
 
21
+ https://github.com/user-attachments/assets/f31c11d8-3fe6-464f-8f02-56645b4ce3a6
22
+
21
23
  ## Markdown support
22
24
 
23
25
  - Paragraphs, bullets and numbered lists, with bold, italic and links
24
- - Tables
26
+ - Tables, images and speaker notes
25
27
  - Code, with [Shiki](https://shiki.style) syntax highlighting
26
28
  - [Mermaid](https://mermaid.js.org) diagrams
27
- - Images, from the theme's asset catalog or a file path
28
- - Speaker notes
29
29
 
30
- Full syntax in [syntax.md](theme-package/syntax.md).
30
+ Full syntax in [markdown.md](docs/markdown.md).
31
31
 
32
32
  ## Requirements
33
33
 
34
34
  Node 23.6 or later. Mermaid diagrams need Chrome on the machine; tycoslide finds an installed one, or run `npx playwright install chromium-headless-shell`.
35
+
36
+ ## Thanks
37
+
38
+ tycoslide was inspired by [pptx-automizer](https://github.com/singerla/pptx-automizer), which it uses under the hood, and by [Slidev](https://sli.dev) and its markdown slides.
@@ -0,0 +1,32 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * One image a theme offers deck authors. `fit` is the value an author copies
4
+ * into the image's title, so it is already a title value.
5
+ */
6
+ declare const AssetEntrySchema: z.ZodObject<{
7
+ path: z.ZodString;
8
+ fit: z.ZodEnum<{
9
+ readonly Contain: "contain";
10
+ readonly Cover: "cover";
11
+ readonly ScaleDown: "scale-down";
12
+ }>;
13
+ description: z.ZodString;
14
+ }, z.core.$strict>;
15
+ declare const AssetCatalogSchema: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
16
+ path: z.ZodString;
17
+ fit: z.ZodEnum<{
18
+ readonly Contain: "contain";
19
+ readonly Cover: "cover";
20
+ readonly ScaleDown: "scale-down";
21
+ }>;
22
+ description: z.ZodString;
23
+ }, z.core.$strict>>>;
24
+ export type AssetEntry = z.infer<typeof AssetEntrySchema>;
25
+ export type AssetCatalog = z.infer<typeof AssetCatalogSchema>;
26
+ /**
27
+ * Read and validate a theme's image catalog, `assets.json`: what a deck-writing
28
+ * agent searches, copies images from, and takes each image's fit from. It is
29
+ * authored with the theme and required; a theme with no images has `{}`.
30
+ */
31
+ export declare function loadAssetCatalog(themeDir: string): AssetCatalog;
32
+ export {};
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import * as z from "zod";
4
+ import { ImageFit, strict } from "../index.js";
5
+ import { ASSETS_FILE } from "./files.js";
6
+ /**
7
+ * One image a theme offers deck authors. `fit` is the value an author copies
8
+ * into the image's title, so it is already a title value.
9
+ */
10
+ const AssetEntrySchema = strict({
11
+ path: z.string(),
12
+ fit: z.enum(ImageFit),
13
+ description: z.string(),
14
+ });
15
+ // `{ category: { name: entry } }`. Both record levels are open (the names are the
16
+ // theme's own); only the entry is strict.
17
+ const AssetCatalogSchema = z.record(z.string(), z.record(z.string(), AssetEntrySchema));
18
+ /**
19
+ * Read and validate a theme's image catalog, `assets.json`: what a deck-writing
20
+ * agent searches, copies images from, and takes each image's fit from. It is
21
+ * authored with the theme and required; a theme with no images has `{}`.
22
+ */
23
+ export function loadAssetCatalog(themeDir) {
24
+ const path = join(themeDir, ASSETS_FILE);
25
+ let raw;
26
+ try {
27
+ raw = JSON.parse(readFileSync(path, "utf-8"));
28
+ }
29
+ catch {
30
+ throw new Error(`Image catalog not found or invalid JSON: ${path}`);
31
+ }
32
+ const result = AssetCatalogSchema.safeParse(raw);
33
+ if (!result.success) {
34
+ throw new Error(`${ASSETS_FILE}: invalid image catalog\n${z.prettifyError(result.error)}`);
35
+ }
36
+ return result.data;
37
+ }
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ /** The installed tycoslide package: where its shipped files live, and what a skill pins. */
3
+ export type ToolPackage = {
4
+ root: string;
5
+ name: string;
6
+ version: string;
7
+ };
8
+ /** Register the agent layer's commands on the CLI program. */
9
+ export declare function registerAgentCommands(program: Command, tool: ToolPackage): void;
@@ -0,0 +1,57 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, posix, resolve } from "node:path";
3
+ import { loadThemeConfig } from "../index.js";
4
+ import { loadAssetCatalog } from "./catalog.js";
5
+ import { extractTemplate, summarizeExtraction } from "./extract.js";
6
+ import { ASSETS_DIR, ASSETS_FILE, MANIFEST_FILE, PACKAGE_JSON, SKILL_FILE, SKILL_ZIP_EXT, TEMPLATE_FILE, THEME_CONFIG, THEME_PACKAGE_DIR, } from "./files.js";
7
+ import { generateManifest } from "./manifest.js";
8
+ import { renameSkill, skillPackageJson, zipDir } from "./skill.js";
9
+ /** The CLI's line for something written, with an optional note on what it holds. */
10
+ function wrote(target, detail) {
11
+ console.log(detail ? `WROTE ${target} ${detail}` : `WROTE ${target}`);
12
+ }
13
+ /** Register the agent layer's commands on the CLI program. */
14
+ export function registerAgentCommands(program, tool) {
15
+ const skillMdPath = resolve(tool.root, THEME_PACKAGE_DIR, SKILL_FILE);
16
+ program
17
+ .command("package")
18
+ .description("Zip the theme into an Agent Skill for AI agents")
19
+ .option(`-c, --config <path>`, "path to theme config file", THEME_CONFIG)
20
+ .action(async (opts) => {
21
+ const config = loadThemeConfig(resolve(process.cwd(), opts.config));
22
+ const catalog = loadAssetCatalog(config.rootDir);
23
+ const themePkg = JSON.parse(readFileSync(resolve(process.cwd(), PACKAGE_JSON), "utf-8"));
24
+ if (!themePkg.name) {
25
+ throw new Error(`Cannot name the skill: the theme's ${PACKAGE_JSON} has no "name" field.`);
26
+ }
27
+ // basename drops any npm scope, e.g. "@acme/acme-slides" -> "acme-slides".
28
+ const skillName = basename(themePkg.name);
29
+ let skillMd;
30
+ try {
31
+ skillMd = renameSkill(readFileSync(skillMdPath, "utf-8"), skillName);
32
+ }
33
+ catch (err) {
34
+ throw new Error(`${skillMdPath}: ${err.message}`);
35
+ }
36
+ // Bundle the WHOLE theme so the skill is self-contained: unzip ->
37
+ // `npm install` (pulls the engine, its docs and its deps) -> `npx tycoslide build`.
38
+ const generated = {
39
+ [PACKAGE_JSON]: skillPackageJson(themePkg, tool),
40
+ [SKILL_FILE]: skillMd,
41
+ [MANIFEST_FILE]: generateManifest(config),
42
+ };
43
+ const zipFile = `${skillName}${SKILL_ZIP_EXT}`;
44
+ const zip = await zipDir(process.cwd(), skillName, config, catalog, [opts.config, ASSETS_FILE], generated);
45
+ writeFileSync(resolve(process.cwd(), zipFile), zip);
46
+ wrote(zipFile);
47
+ });
48
+ program
49
+ .command("extract")
50
+ .description(`Read a template for a theme: write ${TEMPLATE_FILE}, and copy its images into ${ASSETS_DIR}/`)
51
+ .argument("<template>", "path to the .pptx template")
52
+ .action(async (template) => {
53
+ const summary = summarizeExtraction(await extractTemplate(resolve(process.cwd(), template), process.cwd()));
54
+ wrote(TEMPLATE_FILE, summary.template);
55
+ wrote(ASSETS_DIR + posix.sep, summary.images);
56
+ });
57
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Extract: read a template for a theme in one pass. Writes the inventory, with the
3
+ * images it copied, to `template.json`, and copies the template's images into
4
+ * `assets/`, both in the theme folder.
5
+ */
6
+ import { type Inventory } from "./inventory.js";
7
+ import { type MediaImage } from "./media.js";
8
+ export type TemplateImage = MediaImage & {
9
+ /** Where the image is in the theme folder; for a duplicate, the copy that was kept. Absent when missing. */
10
+ path?: string;
11
+ };
12
+ /** The contents of `template.json`. */
13
+ export type TemplateFacts = Inventory & {
14
+ images: TemplateImage[];
15
+ };
16
+ /** Read `templatePath`, write `template.json` and copy the images into `themeDir`. */
17
+ export declare function extractTemplate(templatePath: string, themeDir: string): Promise<TemplateFacts>;
18
+ /** What was written, in a phrase each: the template's facts, and the images. */
19
+ export declare function summarizeExtraction(facts: TemplateFacts): {
20
+ template: string;
21
+ images: string;
22
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Extract: read a template for a theme in one pass. Writes the inventory, with the
3
+ * images it copied, to `template.json`, and copies the template's images into
4
+ * `assets/`, both in the theme folder.
5
+ */
6
+ import { writeFileSync } from "node:fs";
7
+ import { join, posix } from "node:path";
8
+ import { ASSETS_DIR, jsonFile, TEMPLATE_FILE } from "./files.js";
9
+ import { readInventory } from "./inventory.js";
10
+ import { copyImages, MediaOutcome } from "./media.js";
11
+ import { Presentation } from "./pptx.js";
12
+ /** Read `templatePath`, write `template.json` and copy the images into `themeDir`. */
13
+ export async function extractTemplate(templatePath, themeDir) {
14
+ const presentation = await Presentation.open(templatePath);
15
+ const inventory = await readInventory(presentation);
16
+ const images = await copyImages(presentation, join(themeDir, ASSETS_DIR));
17
+ const facts = {
18
+ ...inventory,
19
+ images: images.map((image) => {
20
+ const kept = image.outcome === MediaOutcome.Missing ? undefined : (image.duplicateOf ?? image.file);
21
+ return kept ? { ...image, path: posix.join(ASSETS_DIR, kept) } : image;
22
+ }),
23
+ };
24
+ writeFileSync(join(themeDir, TEMPLATE_FILE), jsonFile(facts));
25
+ return facts;
26
+ }
27
+ /** What was written, in a phrase each: the template's facts, and the images. */
28
+ export function summarizeExtraction(facts) {
29
+ const count = (outcome) => facts.images.filter((image) => image.outcome === outcome).length;
30
+ const template = [
31
+ counted(facts.slides.length, "slide"),
32
+ counted(facts.duplicates.length, "duplicate group"),
33
+ counted(facts.embeddedFonts.length, "embedded typeface"),
34
+ ];
35
+ // [count, noun, what happened, report even when none]
36
+ const images = [
37
+ [count(MediaOutcome.Copied), "image", "copied", true],
38
+ [count(MediaOutcome.Present), "image", "already present", false],
39
+ [count(MediaOutcome.Duplicate), "identical image", "skipped", false],
40
+ [count(MediaOutcome.Missing), "image", "missing from the template", false],
41
+ ];
42
+ return {
43
+ template: template.join(LIST_SEPARATOR),
44
+ images: images
45
+ .filter(([n, , , always]) => always || n > 0)
46
+ .map(([n, noun, state]) => `${counted(n, noun)} ${state}`)
47
+ .join(LIST_SEPARATOR),
48
+ };
49
+ }
50
+ const LIST_SEPARATOR = ", ";
51
+ /** `1 slide`, `3 slides`. */
52
+ function counted(n, noun) {
53
+ return `${n} ${noun}${n === 1 ? "" : "s"}`;
54
+ }
@@ -11,13 +11,23 @@ export declare const ASSETS_FILE = "assets.json";
11
11
  /**
12
12
  * The one archive a packaged theme's declared assets ship inside. Entries are
13
13
  * stored at theme-relative POSIX paths and never rewritten: packaging writes
14
- * them, building expands them. Zip format, deliberately NOT named .zip: the
14
+ * them, and an agent unzips them. Zip format, deliberately NOT named .zip: the
15
15
  * upload host rejects nested .zip entries by extension while accepting .pptx,
16
16
  * which is also a zip -- so the archive travels under a neutral name.
17
17
  */
18
18
  export declare const ASSETS_ARCHIVE = "assets.dat";
19
19
  export declare const SKILL_FILE = "SKILL.md";
20
- export declare const SYNTAX_FILE = "syntax.md";
21
20
  export declare const THEME_PACKAGE_DIR = "theme-package";
22
- /** The manifest a packaged skill installs from, authored rather than copied. */
21
+ /**
22
+ * npm's package manifest: the theme's, which names the skill, and the packaged
23
+ * skill's own, which it installs from (authored rather than copied).
24
+ */
23
25
  export declare const PACKAGE_JSON = "package.json";
26
+ /** The packaged skill is one zip, named `<skill name>.zip`. */
27
+ export declare const SKILL_ZIP_EXT = ".zip";
28
+ /** What `tycoslide extract` learns about a template, written in the theme folder. */
29
+ export declare const TEMPLATE_FILE = "template.json";
30
+ /** Where a theme keeps its images, and where `tycoslide extract` copies a template's. */
31
+ export declare const ASSETS_DIR = "assets";
32
+ /** `value` as the contents of a JSON file the agent layer writes: indented, with a final newline. */
33
+ export declare function jsonFile(value: unknown): string;
@@ -11,7 +11,7 @@ export const ASSETS_FILE = "assets.json";
11
11
  /**
12
12
  * The one archive a packaged theme's declared assets ship inside. Entries are
13
13
  * stored at theme-relative POSIX paths and never rewritten: packaging writes
14
- * them, building expands them. Zip format, deliberately NOT named .zip: the
14
+ * them, and an agent unzips them. Zip format, deliberately NOT named .zip: the
15
15
  * upload host rejects nested .zip entries by extension while accepting .pptx,
16
16
  * which is also a zip -- so the archive travels under a neutral name.
17
17
  */
@@ -20,9 +20,22 @@ export const ASSETS_ARCHIVE = "assets.dat";
20
20
  // the root of a skill folder, and a case-sensitive filesystem will not find any
21
21
  // other.
22
22
  export const SKILL_FILE = "SKILL.md";
23
- export const SYNTAX_FILE = "syntax.md";
24
23
  // The folder holding the SKILL.md template copied into each packaged theme, kept
25
24
  // out of the repo root so skill installers do not mistake it for this repo's own skill.
26
25
  export const THEME_PACKAGE_DIR = "theme-package";
27
- /** The manifest a packaged skill installs from, authored rather than copied. */
26
+ /**
27
+ * npm's package manifest: the theme's, which names the skill, and the packaged
28
+ * skill's own, which it installs from (authored rather than copied).
29
+ */
28
30
  export const PACKAGE_JSON = "package.json";
31
+ /** The packaged skill is one zip, named `<skill name>.zip`. */
32
+ export const SKILL_ZIP_EXT = ".zip";
33
+ /** What `tycoslide extract` learns about a template, written in the theme folder. */
34
+ export const TEMPLATE_FILE = "template.json";
35
+ /** Where a theme keeps its images, and where `tycoslide extract` copies a template's. */
36
+ export const ASSETS_DIR = "assets";
37
+ const JSON_INDENT = 2;
38
+ /** `value` as the contents of a JSON file the agent layer writes: indented, with a final newline. */
39
+ export function jsonFile(value) {
40
+ return `${JSON.stringify(value, null, JSON_INDENT)}\n`;
41
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Inventory: what a template holds, in the terms `theme.json` needs. The slide
3
+ * size, the colour and font schemes, the embedded typefaces; each slide's
4
+ * position, layout, background tone and shapes (with frames in EMU, as
5
+ * `theme.json` takes them); and which slides share the same geometry.
6
+ */
7
+ import { type Frame } from "../index.js";
8
+ import { type Presentation } from "./pptx.js";
9
+ /** The twelve colour-scheme slots, in the order PowerPoint lists them. */
10
+ export declare const SCHEME_SLOTS: readonly ["dk1", "lt1", "dk2", "lt2", "accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "hlink", "folHlink"];
11
+ type SchemeSlot = (typeof SCHEME_SLOTS)[number];
12
+ /** A background's tone, as a layout's `variant` would describe it; `image` for a picture fill. */
13
+ export declare const Background: {
14
+ readonly Light: "light";
15
+ readonly Dark: "dark";
16
+ readonly Image: "image";
17
+ readonly Unknown: "unknown";
18
+ };
19
+ export type Background = (typeof Background)[keyof typeof Background];
20
+ /** Scheme slot → `#RRGGBB`. */
21
+ type SchemeColors = Partial<Record<SchemeSlot, string>>;
22
+ export type ColorScheme = {
23
+ name: string;
24
+ } & SchemeColors;
25
+ export type FontScheme = {
26
+ name: string;
27
+ major?: string;
28
+ minor?: string;
29
+ };
30
+ /** A shape's kind. A fillable shape's kind is the `theme.json` block type that fills it. */
31
+ export declare const ShapeKind: {
32
+ readonly Text: "text";
33
+ readonly Image: "image";
34
+ readonly Table: "table";
35
+ readonly Group: "group";
36
+ readonly Other: "other";
37
+ };
38
+ export type ShapeKind = (typeof ShapeKind)[keyof typeof ShapeKind];
39
+ /** Where a placeholder that doesn't position itself takes its frame from. */
40
+ export declare const FrameSource: {
41
+ readonly Layout: "layout";
42
+ readonly Master: "master";
43
+ };
44
+ export type FrameSource = (typeof FrameSource)[keyof typeof FrameSource];
45
+ export type InventoryShape = {
46
+ /** The shape's name; a group's members are prefixed with the group's name and `/`. */
47
+ name: string;
48
+ kind: ShapeKind;
49
+ /** In EMU, as `theme.json` takes it. */
50
+ frame?: Frame;
51
+ frameFrom?: FrameSource;
52
+ rows?: number;
53
+ cols?: number;
54
+ /**
55
+ * The start of the shape's text, with `¶` between paragraphs and `↵` for line
56
+ * breaks: where a parameter template puts `\n`.
57
+ */
58
+ text: string;
59
+ };
60
+ export type InventorySlide = {
61
+ /** The number in the part name, `ppt/slides/slideN.xml`: what `theme.json`'s `slideNumber` means. */
62
+ slide: number;
63
+ /** 1-based position in presentation order (how rendered previews are numbered); absent when not shown. */
64
+ position?: number;
65
+ layout?: string;
66
+ background: Background;
67
+ shapes: InventoryShape[];
68
+ };
69
+ export type Inventory = {
70
+ slideSize: {
71
+ cx: number;
72
+ cy: number;
73
+ };
74
+ colorScheme: ColorScheme;
75
+ fontScheme: FontScheme;
76
+ embeddedFonts: string[];
77
+ slides: InventorySlide[];
78
+ /** Groups of slide numbers whose shapes have the same kinds and frames, to within a point. */
79
+ duplicates: number[][];
80
+ };
81
+ /** Read a template's inventory. */
82
+ export declare function readInventory(presentation: Presentation): Promise<Inventory>;
83
+ export {};